<?php
class Index{
	const indexCountKey=5;

	
	private $count=1000000;
	private $shmIndex;
	private $shmSystem;
	private $semShmSystem;
	//const bufferCount=1000000;
	const bufferCount=5;
	function __construct($shmIndex,$shmSystem,$semShmSystem,$clear=false) {
		$this->shmIndex=$shmIndex;
		$this->shmSystem=$shmSystem;
		$this->semShmSystem=$semShmSystem;
		if ($clear)
			$this->count(null);
	}

	function shiftMemoryDown($index){
		$count=$this->count();
		$maxI=intval(floor(($count-$index-1)/self::bufferCount));
		for($i=$maxI;$i>=0;$i--){
			if($i==$maxI)
				$delta=(($count-$index-1)%self::bufferCount)+1;
			else
				$delta=self::bufferCount;
				var_dump('delta',$i,$index+$i*self::bufferCount,$delta);
			$res=shmop_read($this->shmIndex,($index+$i*self::bufferCount)*8,($delta)*8);		
			shmop_write($this->shmIndex,$res,($index+$i*self::bufferCount+1)*8);
		}
	}


	function shiftMemoryUp($index){
		$count=$this->count();
		$maxI=intval(floor(($count-$index)/self::bufferCount));
		//var_dump($count,$index,$maxI);
		for($i=0;$i<=$maxI;$i++){
			var_dump('i',$i);
			if($i==$maxI){
				$delta=(($count-$index-2)%self::bufferCount)+1;
				//var_dump($delta);
			}
			else
				$delta=self::bufferCount;
				//var_dump('start',($index+$i*self::bufferCount+1)*8,($delta)*8);
			$res=shmop_read($this->shmIndex,($index+$i*self::bufferCount+1)*8,($delta)*8);	
			//var_dump('start');
			shmop_write($this->shmIndex,$res,($index+$i*self::bufferCount)*8);
			//var_dump('end');
		}
	}

	function delete($key){
		$res=$this->searchIndex($key,false);
		$count=$this->count();
		if($res[1]===1 and $res[0]<($count-1)){
			echo 'start';
			$this->shiftMemoryUp($res[0]);
			echo 'end';
		}
		if($res[1]===1)
			$this->count(-1);	
	}
	function set($key,$value){
		$res=$this->searchIndex($key,false);
//		var_dump('index',$key,$res);
		$count=$this->count();
		if($res[1]===0 and $res[0]<$count){
			$this->shiftMemoryDown($res[0]);
		}
		if($res[1]===0)
			$this->count(1);		
		shmop_write($this->shmIndex,pack('i',$key).pack('i',$value),$res[0]*8);
	}
	function count($n=false){
			$c=false;
			sem_acquire($this->semShmSystem);
			if($n===null){
				shm_put_var($this->shmSystem,self::indexCountKey,0);
				$c=true;
			}
			else{
				$c=shm_get_var($this->shmSystem,self::indexCountKey);
				if($n!==false){
					$c=$c+$n;
					shm_put_var($this->shmSystem,self::indexCountKey,$c);
				}
			}	
			sem_release($this->semShmSystem);		
			return $c;					
		}
	function searchIndex($key,$strict=true){
		$first=0;
		$count=$this->count();
		$last=$count;
		while ($first < $last){
			$mid = $first + intval(($last - $first) / 2);
	 
			if ($key <= $this->getIndex($mid)){
				$last = $mid;
			}
			else{
				$first = $mid + 1;
			}
		}
		if ($count!=0 && $this->getIndex($last) == $key && $last<$count){
			if($strict)
				return $last;
			else
				return Array($last,1);
		} else{
			if($strict)
				return false;
			else
				return Array($last,0);
		}
	}
	
	function getIndex($i){
		$res=shmop_read($this->shmIndex,$i*8,4);
		$res=unpack('ikey',$res);
		return $res['key'];
	}
	function get($key){
		
		$offset=$this->searchIndex($key,true);
		//var_dump('get',$key,$offset);
		if($offset===false)
			return false;
			//var_dump($offset);
		$res=shmop_read($this->shmIndex,$offset*8+4,4);
		$res=unpack('iaddr',$res);
		return ($res['addr']);
	}	
}

$IPCkeyShmIndex=ftok(__FILE__, 'u');
if($IPCkeyShmIndex===-1)
	Throw new ErrorException('sharedMemory (index) IPC key create problem');		
$shmIndex=shmop_open($IPCkeyShmIndex, "c", 0644, 102400000);
if($shmIndex===false)
	Throw new ErrorException('sharedMemory attach problem');		

$IPCkeyShmSystem=ftok(__FILE__, 's');
if($IPCkeyShmSystem===-1)
	Throw new ErrorException('sharedMemory (system) IPC key create problem');
$shmSystem=shm_attach($IPCkeyShmSystem,10240);
if ($shmSystem=== false) 
	Throw new ErrorException('sharedMemory (system) problem');


$IPCkeySemShmSystem=ftok(__FILE__, 'e');
if($IPCkeySemShmSystem===-1)
	Throw new ErrorException('semaphore (semShmSystem) IPC key  create problem');				
$semShmSystem=sem_get  ( $IPCkeySemShmSystem, 1);
if ($semShmSystem=== false) 
	Throw new ErrorException('semaphore (semShmSystem) attach problem');

$index=new Index($shmIndex,$shmSystem,$semShmSystem,true);

	$index->set(0,0);
	$index->set(1,1);
/*	$index->set(2,2);
	$index->set(3,3);
	$index->set(4,4);
	$index->set(5,5);	
	$index->set(6,6);
	$index->set(7,7);
	$index->set(8,8);
	$index->set(9,9);
*/


	//$index->delete(2);	

var_dump('!!!',$index->count());
for($i=0;$i<=10;$i++)
	//var_dump('key '.$i,$index->get($i));
	echo('key '.$i.': '.$index->get($i)."\n");	
//exit(1);
$records=1000000;
$t1=mktime();
for($i=0;$i<$records;$i++){
	if($i%1000==0)echo intval($i/1000).' ';
	$index->set($i*2,$i*2);
	//if($i%1000==0)	echo ', '.(mktime()-$t)."  ";
}
echo "\n".'seconds :'.(mktime()-$t1).', and'.round($records/(mktime()-$t1),2)."per second\n";

/*
$t1=mktime();
for($i=500;$i>0;$i--){
	if($i%1000==0)echo intval($i/1000).' ';
	$index->set(rand(0,1000000)*2+1,$i);
}
echo "\n".'seconds :'.(mktime()-$t1).', and'.round(500/(mktime()-$t1),2)."per second\n";
exit(1);
$t1=mktime();
for($i=0;$i<10000000;$i++){
	if($i%1000==0)echo intval($i/1000 );
	$index->get($i);
}
*/
	var_dump($index->count());
$t1=mktime();
for($i=0;$i<1000000;$i++){
	if($i%1000==0)echo intval($i/1000 );
	$index->get($i*2);
}
echo "\n".'seconds :'.(mktime()-$t1)."\n";
//var_dump($index->get(1));


?>